desc:Paraphonic Strings                          T.Rochebois 01/2016
slider1:sl_A=-2.5<-3,1>Attack
slider2:sl_R=-1<-3,1>Release

slider4:sl_bMax=2.4<0,3>Dynamic Max brilliance
slider5:sl_bMin=3.0<0,3>Min brilliance

slider7:lfoDepthMax=0.03<0,0.05>ModWheel Max Vibrato
slider8:lfoDepthMin=0.01<0,0.05>Min Vibrato
// __________________________________________________________________
@init
function init() instance(gate env p dp dpm y lfo dplfo AR)
local(i)(
  gate   = ad; ad += 128;
  env    = ad; ad += 128;
  p      = ad; ad += 128;
  dp     = ad; ad += 128;
  dpm    = ad; ad += 128;
  y      = ad; ad += 128;
  lfo    = ad; ad += 128;
  dplfo  = ad; ad += 128;
  AR     = ad; ad += 2;

  _srate = 1 / srate;
  i = 0; loop(128,
    dp[i] = (4096*440*_srate)*2^((i-69+0.05*(rand(1)-0.5))*(1/12));
    dplfo[i] = 4096 * (5 + rand(2)) * 8 * _srate;
    i += 1;
  );
  sin4096 === 0 ? (
    sin4096 = ad; ad += 4096;
    i = 0; loop(4096, sin4096[i] = sin(i * (2*$pi/4096)); i += 1; );
  );
);
// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
function bProc()
instance(gate p)
local(i p offset msg1 msg2 msg3 modulo status n) (
  while (midirecv(offset, msg1, msg2, msg3)) (
    status = msg1 & $xF0;
    status === $x90 ? ( gate[msg2] = msg3 * (1/127); )
  : status === $x80 ? ( gate[msg2] = 0;              );

    status === $xB0 ? (     
      msg2 === 1  ? expression = msg3*(1/127);
      msg2 === 123 ? ( // all notes off
        n = 0; loop(128, gate[n] = 0; n += 1; );
      );
    );


    co_lfoDepth = lfoDepthMin + expression * (lfoDepthMax - lfoDepthMin); 

    midisend(offset, msg1, msg2, msg3);
  );
// _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
  i = 0;
  modulo = samplesblock * 4096;
  loop(128,
    p[i] -= (p[i] >= modulo) * modulo;
    i += 1;
  );
);
// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
function aProc() 
instance(gate env p dp dpm lfo dplfo y se2 _se2 k AR out bMin bMax_bMin dc) 
local(i b acc)(
  k += 1; k === 8 ? (
    k = 0;
    i = 0; loop(128,
      env[i] !== 0 ? (
        lfo[i] += dplfo[i];
        lfo[i] -= 4096 * (lfo[i] >= 4096);
        dpm[i] = dp[i] * (1 + co_lfoDepth * sin4096[lfo[i]]);
      );
      i += 1;
    );
  );
// _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
  acc = 0;
  se2 = 0;
  i = 0; loop(128,
    env[i] += AR[env[i] > gate[i]] * (gate[i] - env[i]);
    gate[i] !== 0 || env[i] > 0.005 ? (
      se2 += env[i] * env[i];
      b = bMin + env[i] * bMax_bMin;
      p[i] += dpm[i];
      y[i] += 0.5 * ( sin4096[(p[i] + b * y[i]) & (4096-1)] - y[i] );
      acc += env[i] * y[i];
    ) : env[i] = 0;
    i += 1;
  );
  _se2 = invsqrt(se2 + 0.0001);
  out = 0.45*acc * min(1, _se2);
  dc += 0.001 * (out - dc);
  out -= dc;
);
// __________________________________________________________________
// __________________________________________________________________
s.init();
// __________________________________________________________________
@slider
s.AR[0] = 1/(srate*10^sl_A);
s.AR[1] = 1/(srate*10^sl_R);
s.bMin = sl_bMin * 4096/(2*$pi);
s.bMax_bMin = sl_bMax * 4096/(2*$pi) - s.bMin;
// __________________________________________________________________
@block
s.bProc();
// __________________________________________________________________
@sample
s.aProc();
spl0 += s.out;
spl1 += s.out;